package com.cpkso.buss.backend.trade.service;

import java.math.BigDecimal;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Optional;

import javax.transaction.Transactional;

import org.json.JSONArray;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.data.domain.Sort.Direction;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import com.cpkso.buss.backend.exception.GivenNullParamException;
import com.cpkso.buss.backend.exception.NoSuchDataException;
import com.cpkso.buss.backend.trade.dao.TbBuyerOrderDao;
import com.cpkso.buss.backend.trade.dao.TbOrderDao;
import com.cpkso.buss.backend.trade.dao.TbSellerOrderDao;
import com.cpkso.buss.backend.trade.domain.TbBuyerOrder;
import com.cpkso.buss.backend.trade.domain.TbOrder;
import com.cpkso.buss.backend.trade.domain.TbSellerOrder;
import com.cpkso.buss.backend.trade.vo.ActualFee;
import com.cpkso.buss.backend.trade.vo.NoTipListItem;
import com.cpkso.buss.backend.trade.vo.StatisticsConditionVo;
import com.cpkso.buss.backend.trade.vo.StatisticsItemVo;
import com.cpkso.buss.backend.trade.vo.StatisticsListVo;
import com.cpkso.buss.backend.trade.vo.StatisticsSpecification;
import com.cpkso.buss.backend.trade.vo.TbSBOrderVo;
import com.cpkso.buss.backend.user.dao.UserDao;
import com.cpkso.buss.backend.user.domain.User;
import com.runze.base.web.Query;
import com.runze.base.web.Response;

/**
 * TODO 退货的怎么处理？
 * TODO 自己买的东西怎么办？
 * TODO 订单同步当天或某一段时期内的？
 * TODO 每次订单同步一个月的?
 * 
 * 卖家号有订单，买家号没
 * 卖家号没订单，买家号有 - 自己买的
 * 
 * @author wrzhxy@qq.com
 * @date 2018年4月7日
 */
@Service
@Transactional
public class TbOrderService {
	
	static final private int ORDER_ID_LENGTH = 18;
	/**
	 * 保存卖家新订单
	 * @param tbSellerOrder
	 */
	public void createTbSellerOrder(TbSellerOrder tbSellerOrder) throws GivenNullParamException {
		if (tbSellerOrder == null) throw new GivenNullParamException();
		tbSellerOrderDao.save(tbSellerOrder);
	}
	/**
	 * 保存卖家新订单 客户端传送 批量
	 * 如果已经有重复的订单就替换
	 * TODO  更严格的异常判断处理
	 * @param tbSBOrderVo
	 */
	public void createTbSellerOrder(List<TbSBOrderVo> tbSBOrderVo) throws GivenNullParamException, NoSuchDataException {
		if (tbSBOrderVo == null) throw new GivenNullParamException();
System.out.println(tbSBOrderVo.size());	
		// 先根据用户名称获取用户，用于设置卖家订单
		TbSBOrderVo sbOrderVo = tbSBOrderVo.get(0);
		String username = sbOrderVo.getUsername();
		if (!StringUtils.hasText(username)) throw new GivenNullParamException(); 
		User user = userDao.findByNameAndVerify(username, 1);
		if (user == null) throw new NoSuchDataException("无此用户！");
		// 获取订单时间
		// 在本次发送来的订单的时间段内，如果原本有的订单这次没有了，就把订单删除掉
		Date orderEndTime = sbOrderVo.getOrderTime();
		TbSBOrderVo sbOrderVoBegin = tbSBOrderVo.get(tbSBOrderVo.size() - 1);
		Date orderStartTime = sbOrderVoBegin.getOrderTime();
		// 存储订单号
		List<String> orderIds = new ArrayList<>();
		
		// 根据订单vo，设置进列表中批量添加进数据库
		List<TbSellerOrder> solist = new ArrayList<>();
		for (TbSBOrderVo vo : tbSBOrderVo) {
			// 将商品名设置为一个json字符串
			List<String> items = vo.getItems();
			JSONArray js = new JSONArray(items);
			String itemsStr = js.toString();
			
			// 根据订单id判断是否插入新订单
			String orderId = vo.getOrderId();
			TbSellerOrder tboDb = tbSellerOrderDao.findOneByOrderId(orderId);
			if (tboDb == null) {
				// 可插入
				TbSellerOrder tbSellerOrder = new TbSellerOrder(user, vo.getOrderId(), vo.getBuyerId(), vo.getOrderTime(), vo.getActualFee(), itemsStr);
				solist.add(tbSellerOrder);
			} else {
				// 订单id在数据库存在，加入列表，用于查询数据库的失败订单
				orderIds.add(orderId);
				// 不可插入，可更新
				tboDb.setActualFee(vo.getActualFee());
				tboDb.setBuyerId(vo.getBuyerId());
				tboDb.setItems(itemsStr);
				tboDb.setOrderId(vo.getOrderId());
				tboDb.setOrderTime(vo.getOrderTime());
				tboDb.setUser(user);
				solist.add(tboDb);
			}
			tbSellerOrderDao.saveAll(solist);
		}
		// 按日期和订单号查询，去除失败订单
		List<TbSellerOrder> failSellerOrders = tbSellerOrderDao.findByOrderTimeBetweenAndOrderIdNotIn(orderStartTime, orderEndTime, orderIds.toArray(new String[0]));
System.out.println(failSellerOrders);
		tbSellerOrderDao.deleteAll(failSellerOrders);
		List<TbOrder> failOrders = tbOrderDao.findBySellerTimeBetweenAndSellerOrderIdNotIn(orderStartTime, orderEndTime, orderIds.toArray(new String[0]));
System.out.println(failOrders);
		tbOrderDao.deleteAll(failOrders);
	}
	
	/**
	 * 保存买家新订单
	 */
	public void createTbBuyerOrder(TbBuyerOrder tbBuyerOrder) throws GivenNullParamException {
		if (tbBuyerOrder == null) throw new GivenNullParamException();
		tbBuyerOrderDao.save(tbBuyerOrder);
	}
	/**
	 * 保存买家新订单 客户端传送 批量
	 * 如果已经有重复的订单就替换
	 * TODO  更严格的异常判断处理
	 * @param tbSBOrderVo
	 */
	public void createTbBuyerOrder(List<TbSBOrderVo> tbSBOrderVo) throws GivenNullParamException, NoSuchDataException {
		if (tbSBOrderVo == null) throw new GivenNullParamException();
		// 先根据用户名称获取用户，用于设置买家订单
		TbSBOrderVo sbOrderVo = tbSBOrderVo.get(0);
		String username = sbOrderVo.getUsername();
		if (!StringUtils.hasText(username)) throw new GivenNullParamException(); 
		User user = userDao.findByNameAndVerify(username, 1);
		if (user == null) throw new NoSuchDataException("无此用户！");
		// 获取订单时间
		Date orderEndTime = sbOrderVo.getOrderTime();
		TbSBOrderVo sbOrderVoBegin = tbSBOrderVo.get(tbSBOrderVo.size() - 1);
		Date orderStartTime = sbOrderVoBegin.getOrderTime();
		// 存储订单号
		List<String> orderIds = new ArrayList<>();
		
		// 根据订单vo，设置进列表中批量添加进数据库
		List<TbBuyerOrder> bolist = new ArrayList<>();
		for (TbSBOrderVo vo : tbSBOrderVo) {
			// 将商品名设置为一个字符串
			List<String> items = vo.getItems();
			JSONArray js = new JSONArray(items);
			String itemsStr = js.toString();
			// 根据订单id判断是否插入新订单
			String orderId = vo.getOrderId();
			TbBuyerOrder tboDb = tbBuyerOrderDao.findOneByOrderId(orderId);
			if (tboDb == null) {
				// 可插入
				TbBuyerOrder tbBuyerOrder = new TbBuyerOrder(user, vo.getOrderId(), vo.getOrderTime(), vo.getActualFee(), itemsStr);
				bolist.add(tbBuyerOrder);
			} else {
				// 订单id在数据库存在，加入列表，用于查询数据库的失败订单
				orderIds.add(orderId);
				// 不可插入，可更新
				tboDb.setActualFee(vo.getActualFee());
				tboDb.setItems(itemsStr);
				tboDb.setOrderId(vo.getOrderId());
				tboDb.setOrderTime(vo.getOrderTime());
				tboDb.setUser(user);
				bolist.add(tboDb);
			}
			tbBuyerOrderDao.saveAll(bolist);
		}
		// 按日期和订单号查询，去除失败订单
		List<TbBuyerOrder> failBuyerOrders = tbBuyerOrderDao.findByOrderTimeBetweenAndOrderIdNotIn(orderStartTime, orderEndTime, orderIds.toArray(new String[0]));
		System.out.println(failBuyerOrders);
		tbBuyerOrderDao.deleteAll(failBuyerOrders);
		List<TbOrder> failOrders = tbOrderDao.findByBuyerTimeBetweenAndBuyerOrderIdNotIn(orderStartTime, orderEndTime, orderIds.toArray(new String[0]));
		System.out.println(failOrders);
		tbOrderDao.deleteAll(failOrders);
	}
	
	/**
	 * 合并订单
	 * 只合并两个月以内的订单，这样提高效率
	 * - 怎么判定原本匹配但后来不匹配的订单？
	 * - 交易关闭的也发过来 进行判定？
	 * 发过来时按时间排列，如果之前有现在没有的就删除掉，同时删除订单
	 * 数据库是时间倒序排列，说明插入时最新的最先插入，查询的时候取最后一个到第一个的时间区间查询
	 * - 编一条语句？
	 * TODO scheduled
	 */
	@Scheduled(cron = "30 10 1 * * ?")
	public void mergeOrder() throws NoSuchDataException {
System.out.println("lalalal");		
		// 查询卖家订单
		// 查询两个月的？
		// TODO 查询全部
		List<TbSellerOrder> sellerOrders = (List<TbSellerOrder>) tbSellerOrderDao.findAll();
		if (sellerOrders == null || sellerOrders.size() < 1) throw new NoSuchDataException();
		// 用于订单持久化的user
		TbSellerOrder toGetUser = sellerOrders.get(0);
		User userTemp = toGetUser.getUser();
		Optional<User> userOpt = userDao.findById(userTemp.getId());
		User user = userOpt.get();
		
		// 根据卖家订单-买家订单号 查询买家订单
		for (TbSellerOrder tbSellerOrder : sellerOrders) {
//			一个卖家单多个买家单
//			分开计算：用空格隔开
//			18位，
//			接收是不用做判断
//			合并时计算总价格就行
			Double totalBuyerFee = 0.0;
			Date buyerOrderTime = null;
			String buyerIds = tbSellerOrder.getBuyerId();
			String[] buyerIdArr = {buyerIds};
			if (buyerIds.length() != ORDER_ID_LENGTH) {
				buyerIdArr = buyerIds.split(" ");
			}
			for (int i = 0; i < buyerIdArr.length; i++) {
				TbBuyerOrder tbBuyerOrder = tbBuyerOrderDao.findOneByOrderId(buyerIdArr[i]);
				if (tbBuyerOrder == null) continue;
				// TODO 抽取出来工具类
				Double actualFee = tbBuyerOrder.getActualFee();
				BigDecimal actualFeeBD = BigDecimal.valueOf(actualFee);
				BigDecimal totalBuyerFeeBD = BigDecimal.valueOf(totalBuyerFee);
				totalBuyerFeeBD = totalBuyerFeeBD.add(actualFeeBD);
				totalBuyerFee = totalBuyerFeeBD.doubleValue();
				if (buyerOrderTime == null)
					buyerOrderTime = tbBuyerOrder.getOrderTime();
			}
			if (buyerOrderTime == null) continue;
			// 同步进新订单 - 若已存在则更新，若不存在则插入
			TbOrder tbOrder = tbOrderDao.findOneBySellerOrderId(tbSellerOrder.getOrderId());
			if (tbOrder == null) {
				tbOrder = new TbOrder(
						tbSellerOrder.getBuyerId(), 
						tbSellerOrder.getOrderId(), 
						buyerOrderTime, 
						tbSellerOrder.getOrderTime(),
						totalBuyerFee,
						tbSellerOrder.getActualFee(),
						tbSellerOrder.getItems(),
						"",
						user);
			} else {
				// 更新订单
				tbOrder.setBuyerOrderId(tbSellerOrder.getBuyerId());
				tbOrder.setBuyerPrice(totalBuyerFee);
				tbOrder.setBuyerTime(buyerOrderTime);
				tbOrder.setItems(tbSellerOrder.getItems());
				tbOrder.setSellerOrderId(tbSellerOrder.getOrderId());
				tbOrder.setSellerPrice(tbSellerOrder.getActualFee());
				tbOrder.setSellerTime(tbSellerOrder.getOrderTime());
				tbOrder.setUser(user);
			}
			tbOrderDao.save(tbOrder);
		}
	}
	/**
	 * TODO 业务统计
	 * 时间区间
	 * 订单号
	 * 
	 * 盈利：
	 * 当天盈利
	 * 总盈利
	 * 当月盈利
	 * 某月盈利
	 * 个人盈利
	 * 
	 * 卖家-买家号差价
	 * 
	 * 
	 */
	public Response statistics(StatisticsConditionVo scv) {
		if (scv == null) scv = new StatisticsConditionVo();
		// 时间日期做些处理
		// 结束日期处理按235959
		Date beginDate = scv.getBeginDate();
		Date endDate = scv.getEndDate();
		SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
		if (endDate == null && beginDate != null) {
			// 若起始日期没
			String format = sdf.format(beginDate);
			String morningStr = format + " 00:00:00";
			String nightStr = format + " 23:59:59";
			sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
			Date morning = null;
			Date night = null;
			try {
				morning = sdf.parse(morningStr);
				night = sdf.parse(nightStr);
				scv.setBeginDate(morning);
				scv.setEndDate(night);
			} catch (ParseException e) {
				e.printStackTrace();
			}
		} else if (endDate != null && beginDate != null) {
			String format = sdf.format(endDate);
			String nightStr = format + " 23:59:59";
			sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
			Date night = null;
			try {
				night = sdf.parse(nightStr);
				scv.setEndDate(night);
			} catch (ParseException e) {
				e.printStackTrace();
			}
		} else {
			scv.setEndDate(null);
		}
		// 姓名去掉空格
		if (StringUtils.hasText(scv.getUsername())) {
			scv.setUsername(scv.getUsername().trim());
		}
		// 按什么排序
		Sort sort = new Sort(Direction.DESC, "id");
        Pageable pageable = PageRequest.of(scv.getPageNo() - 1, scv.getPageSize(), sort); 
		StatisticsSpecification ss = new StatisticsSpecification(scv);
		// 查询
		Page<TbOrder> page = tbOrderDao.findAll(ss, pageable);
//		List<TbOrder> orders = tbOrderDao.findAll();
		List<TbOrder> orders = page.getContent();
		List<StatisticsItemVo> list = new ArrayList<>();
		for (TbOrder to : orders) {
			User user = to.getUser();
			double deprive = user.getCharge();
			String username = user.getName();
			BigDecimal depriveBD = BigDecimal.valueOf(deprive);
			BigDecimal earnBD = BigDecimal.valueOf(to.getSellerPrice()).subtract(BigDecimal.valueOf(to.getBuyerPrice()));
			double earn = earnBD.doubleValue();
			// deprive * earn
			BigDecimal dmeBD = depriveBD.multiply(earnBD);	//, MathContext.DECIMAL64
			double dme = dmeBD.doubleValue();
			// earn * (1 - deprive)
			BigDecimal esdBD = earnBD.subtract(dmeBD);
			double esd = esdBD.doubleValue();
			StatisticsItemVo siv = new StatisticsItemVo(
					to.getId(),
					to.getBuyerTime(), 
					to.getItems(), 
					earn, 
					dme, 
					esd, 
					username, 
					deprive == 0.5 ? "主管" : "非主管", 
					to.getSellerOrderId(), 
					to.getBuyerOrderId(), 
					to.getSellerPrice(), 
					to.getTip());
			list.add(siv);
		}
		int size = page.getSize();
		int totalPages = page.getTotalPages();
		scv.setPageSize(size);
		scv.setMaxPageNo(totalPages);
		scv.setTotalNum(page.getTotalElements());
		
		// 统计结果
		StatisticsListVo bedeprive = tbOrderDao.bedeprive(scv);
		bedeprive.setBeginDate(beginDate);
		bedeprive.setEndDate(endDate);
		bedeprive.setItems(list);
		
		return new Response(bedeprive, scv);
	}
	/**
	 * 黑名单 没备注
	 */
	public Response notips(Query query) {
		int pageNo = query.getPageNo();
		int pageSize = query.getPageSize();
        Sort sort = new Sort(Direction.DESC, "id");
        Pageable pageable = PageRequest.of(pageNo - 1, pageSize, sort); 
        Page<NoTipListItem> list = tbSellerOrderDao.noTipList(pageable);
        return new Response(list.getContent(), new Query(pageNo, pageSize, list.getTotalPages()));
	}
	/**
	 * 退差价
	 * 总数减去fee，若无备注自动备注被修改过
	 * @param fee
	 */
	public void updatePrice(ActualFee fee) throws GivenNullParamException, NoSuchDataException {
		if (fee == null || fee.getId() == null) throw new GivenNullParamException();
		Optional<TbOrder> orderOpt = tbOrderDao.findById(fee.getId());
		TbOrder tbOrder = orderOpt.get();
		// 需要一个字段记录最早的金额
		String tip = fee.getTip();
		if (StringUtils.hasText(tip)) {
			tbOrder.setTip(tip + " 改动前：" + tbOrder.getBuyerPrice());
		} else {
			tbOrder.setTip("改动前：" + tbOrder.getBuyerPrice());
		}
		tbOrder.setSellerPrice(fee.getFee());
		tbOrderDao.save(tbOrder);
	}
	@Autowired
	private TbSellerOrderDao tbSellerOrderDao;
	@Autowired
	private TbBuyerOrderDao tbBuyerOrderDao;
	@Autowired
	private TbOrderDao tbOrderDao;
	@Autowired
	private UserDao userDao;
}
